home *** CD-ROM | disk | FTP | other *** search
/ Language/OS - Multiplatform Resource Library / LANGUAGE OS.iso / smaltalk / manchest.lha / MANCHESTER / manchester / 4.1 / calculator.st < prev    next >
Text File  |  1993-07-24  |  22KB  |  794 lines

  1. "    NAME        calculator
  2.     AUTHOR        various (see summary)
  3.     FUNCTION    A simple four-function calculator
  4.     ST-VERSION    4.1
  5.     PREREQUISITES    
  6.     CONFLICTS    
  7.     DISTRIBUTION    world
  8.     VERSION        1
  9.     DATE         24 Nov 1992
  10. SUMMARY
  11. This is a simple four-function calculator.
  12.  
  13. The original for 2.X, was by Trevor Hopkins (tph@cs.man.ac.uk).  it
  14. was subsequently ported to R4 by Bernard Horan
  15. (bernard@com.morgan.is).  A few minor mods were made by Mario Wolczko
  16. (mario@cs.man.ac.uk).   Somewhere along the line the class comments
  17. were lost.
  18.  
  19. While the 2.X version was a good example of view construction, this
  20. may not be such an examplar for R4...
  21. "
  22. 'From Objectworks\Smalltalk(R), Release 4.1 of 15 April 1992 on 24 November 1992 at 1:14:54 pm'!
  23.  
  24.  
  25.  
  26. DialogView subclass: #KeypadView
  27.     instanceVariableNames: ''
  28.     classVariableNames: 'KeyComponents TextStyle '
  29.     poolDictionaries: ''
  30.     category: 'Calculator'!
  31.  
  32.  
  33. !KeypadView methodsFor: 'accessing'!
  34.  
  35. defaultController
  36.     ^KeypadViewController new!
  37.  
  38. dispatchesKeyboard
  39.     ^true!
  40.  
  41. model: aCalculator
  42.     super model: aCalculator.
  43.     self buildSubViews!
  44.  
  45. subViewContainingCharacter: aCharacter 
  46.     "Answer the receiver's subView that corresponds to the key, 
  47.     aCharacter.
  48.     Answer nil if no subView is selected by aCharacter."
  49.     "This is an atrocious hack, and is based on a the way the views are created 
  50.     (see private method). This is because I am a subclass of DialogView. The long 
  51.     concatenated unary messages pluck out my wrappers (on ButtonViews)"
  52.  
  53.     self components first component components reverseDo: [:aWrapper |
  54.         (aWrapper component containsKey: aCharacter asLowercase)
  55.             ifTrue: [^aWrapper component]].
  56.     ^nil! !
  57.  
  58. !KeypadView methodsFor: 'private'!
  59.  
  60. buildSubViews
  61.     "Build my buttons"
  62.     "If you change this you may have to change the method #subViewContainingCharacter.
  63.     The order of the elements has to match the order of the characters, and gives the order of the buttons on the display"
  64.     | elements chars |
  65.     elements := #(#add7 #add8 #add9 #clearEntry #clear #add4 #add5 #add6 #multiply #divide #add1 #add2 #add3 #add #subtract #add0 #point #raiseTo #changeSign #equals ).
  66.     chars := #($7 $8 $9 $e $c
  67.                 $4 $5 $6 $* $/
  68.                 $1 $2 $3 $+ $-
  69.                 $0 $. $^ $s $=).
  70.     self
  71.         addAll: (1 to: elements size)
  72.         inRows: 4
  73.         fromX: 0
  74.         toX: 1
  75.         collect: 
  76.             [:i | 
  77.             | button view wrapper  element|
  78.             element := elements at: i.
  79.             button := (PluggableAdaptor on: model)
  80.                         getBlock: [:m | false]
  81.                         putBlock: [:m :a | m perform: element]
  82.                         updateBlock: [:m :a :p | false].
  83.             view := ButtonView model: button.
  84.             view beVisual: (KeyComponents at: element).
  85.             view key: (chars at: i).
  86.             view controller beTriggerOnUp.
  87.             wrapper := BorderedWrapper on: view.
  88.             wrapper insideColor: ColorValue white.
  89.             wrapper]! !
  90. "-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- "!
  91.  
  92. KeypadView class
  93.     instanceVariableNames: ''!
  94.  
  95.  
  96. !KeypadView class methodsFor: 'class initialization'!
  97.  
  98. initialize
  99.     "KeypadView initialize."
  100.  
  101.     self initializeTextStyle.
  102.     self initializeKeyComponents!
  103.  
  104. initializeKeyComponents
  105.     "KeypadView initializeKeyComponents."
  106.     "A Dictionary of VisualComponents to be used for the buttons"
  107.  
  108.     | pixmap image |
  109.     KeyComponents := Dictionary new.
  110.     0 to: 9 do: [:number | KeyComponents at: ('add' , number printString) asSymbol put: (ComposedText withText: number printString asText style: self textStyle)].
  111.     pixmap := Pixmap extent: 15 @ 15.
  112.     pixmap graphicsContext
  113.         displayWedgeBoundedBy: (pixmap bounds insetBy: 3 @ 3)
  114.         startAngle: 0
  115.         sweepAngle: 360.
  116.     KeyComponents at: #clearEntry put: pixmap asImage.
  117.     KeyComponents at: #equals put: (ComposedText withText: '='  asText style: self textStyle).
  118.     KeyComponents at: #add put: (ComposedText withText: '+'  asText style: self textStyle).
  119.     KeyComponents at: #subtract put: (ComposedText withText: '-'  asText style: self textStyle).
  120.     KeyComponents at: #multiply put: (ComposedText withText: 'X'  asText style: self textStyle).
  121.     KeyComponents at: #divide put: (ComposedText withText: '/'  asText style: self textStyle).
  122.     image := Image
  123.                 extent: 10 @ 14
  124.                 depth: 1
  125.                 palette: MappedPalette whiteBlack
  126.                 words: #(12288 12288 64512 64704 12672 13056 1536 3072 6144 12288 26560 51136 0 0 ).
  127.     KeyComponents at: #changeSign put: image.
  128.     image := Image
  129.                 extent: 10 @ 14
  130.                 depth: 1
  131.                 palette: MappedPalette whiteBlack
  132.                 words: #(0 1088 1088 640 256 256 512 35328 34816 20480 8192 20480 34816 34816 ).
  133.     KeyComponents at: #raiseTo put: image.
  134.     KeyComponents at: #point put: (ComposedText withText: '.'  asText style: self textStyle).
  135.     KeyComponents at: #clear put: (ComposedText withText: 'C'  asText style: self textStyle)!
  136.  
  137. initializeTextStyle
  138.     "The textStyle used when creating (some of) the buttons. See method #initializeKeyComponents"
  139.     TextStyle := (TextAttributes defaultFontQuery: ((FontDescription new) fixedWidth: false; serif: false; italic: false; boldness: 0.5; pixelSize: 18))
  140.                 lineGrid: 20;
  141.                 baseline: 15! !
  142.  
  143. !KeypadView class methodsFor: 'private'!
  144.  
  145. textStyle
  146.     ^TextStyle! !
  147.  
  148.  
  149. Model subclass: #Calculator
  150.     instanceVariableNames: 'displayValue accumulatorValue currentOperation doneOperation noOfDigits errorFlag pointPlaces trailingZeros leftJustify '
  151.     classVariableNames: 'DefaultNoOfDigits '
  152.     poolDictionaries: ''
  153.     category: 'Calculator'!
  154.  
  155.  
  156. !Calculator methodsFor: 'initialize-release'!
  157.  
  158. initialize
  159.     "Initialize the receiver."
  160.  
  161.     displayValue := 0.
  162.     accumulatorValue := 0.
  163.     currentOperation := nil.
  164.     doneOperation := true.
  165.     errorFlag := false.
  166.     pointPlaces := 0.
  167.     trailingZeros := 0.
  168.     leftJustify := true.
  169.     noOfDigits := DefaultNoOfDigits! !
  170.  
  171. !Calculator methodsFor: 'accessing'!
  172.  
  173. displayValue
  174.     "Answers with the current display value."
  175.  
  176.     ^displayValue!
  177.  
  178. errorFlag
  179.     "Answer the error flag value."
  180.  
  181.     ^errorFlag!
  182.  
  183. leftJustify
  184.     "Answer the current value of the left justify flag."
  185.  
  186.     ^leftJustify!
  187.  
  188. leftJustify: aBoolean
  189.     "Set the current value of the left justify flag."
  190.  
  191.     leftJustify := aBoolean.
  192.     self changed!
  193.  
  194. noOfDigits
  195.     "Answer the number of digits represented."
  196.  
  197.     ^noOfDigits!
  198.  
  199. noOfDigits: aNumber
  200.     "Sets the number of digits represented to aNumber."
  201.  
  202.     noOfDigits := aNumber.
  203.     self changed!
  204.  
  205. pointPlaces
  206.     ^pointPlaces!
  207.  
  208. trailingZeros
  209.     "Answer the number of trailing zeros."
  210.  
  211.     ^trailingZeros! !
  212.  
  213. !Calculator methodsFor: 'operation keys'!
  214.  
  215. add
  216.     "Make the current operation addition."
  217.  
  218.     self doOperation.
  219.     currentOperation := #addition.!
  220.  
  221. changeSign
  222.     "Change the sign of the value in the display register."
  223.  
  224.     self makeDisplay: displayValue negated!
  225.  
  226. clear
  227.     "Clear the error flag.  Clear the display and accumulator registers."
  228.  
  229.     errorFlag := false.
  230.     pointPlaces := 0.
  231.     trailingZeros := 0.
  232.     accumulatorValue := 0.
  233.     currentOperation := nil.
  234.     self makeDisplay: 0!
  235.  
  236. clearEntry
  237.     "Zero the display register."
  238.  
  239.     pointPlaces := 0.
  240.     trailingZeros := 0.
  241.     self makeDisplay: 0!
  242.  
  243. divide
  244.     "Make the current operation division."
  245.  
  246.     self doOperation.
  247.     currentOperation := #division!
  248.  
  249. equals
  250.     "Perform the current operation, putting the answer in
  251.      the accumulator. Display the answer."
  252.  
  253.     self performOperation: currentOperation.
  254.     self makeDisplay: accumulatorValue.
  255.     doneOperation := true.
  256.     trailingZeros := 0.
  257.     currentOperation := nil!
  258.  
  259. multiply
  260.     "Make the current operation multiplication."
  261.  
  262.     self doOperation.
  263.     currentOperation := #multiplication!
  264.  
  265. point
  266.     "Insert a decimal point. Sets the number of decimal places to 1."
  267.  
  268.     pointPlaces = 0 ifTrue: [pointPlaces := 1]!
  269.  
  270. raiseTo
  271.     "Make the current operation raise-to-power."
  272.  
  273.     self doOperation.
  274.     currentOperation := #raiseTo!
  275.  
  276. subtract
  277.     "Make the current operation subtraction."
  278.  
  279.     self doOperation.
  280.     currentOperation := #subtraction.! !
  281.  
  282. !Calculator methodsFor: 'digit keys'!
  283.  
  284. add0
  285.     "Add 0 to the display value."
  286.  
  287.     pointPlaces > 0 ifTrue: [trailingZeros := trailingZeros + 1].
  288.     self addToDisplay: 0.!
  289.  
  290. add1
  291.     "Add 1 to the display value."
  292.  
  293.     trailingZeros := 0.
  294.     self addToDisplay: 1.!
  295.  
  296. add2
  297.     "Add 2 to the display value."
  298.  
  299.     trailingZeros := 0.
  300.     self addToDisplay: 2.!
  301.  
  302. add3
  303.     "Add 3 to the display value."
  304.  
  305.     trailingZeros := 0.
  306.     self addToDisplay: 3.!
  307.  
  308. add4
  309.     "Add 4 to the display value."
  310.  
  311.     trailingZeros := 0.
  312.     self addToDisplay: 4.!
  313.  
  314. add5
  315.     "Add 5 to the display value."
  316.  
  317.     trailingZeros := 0.
  318.     self addToDisplay: 5.!
  319.  
  320. add6
  321.     "Add 6 to the display value."
  322.  
  323.     trailingZeros := 0.
  324.     self addToDisplay: 6.!
  325.  
  326. add7
  327.     "Add 7 to the display value."
  328.  
  329.     trailingZeros := 0.
  330.     self addToDisplay: 7.!
  331.  
  332. add8
  333.     "Add 8 to the display value."
  334.  
  335.     trailingZeros := 0.
  336.     self addToDisplay: 8.!
  337.  
  338. add9
  339.     "Add 9 to the display value."
  340.  
  341.     trailingZeros := 0.
  342.     self addToDisplay: 9.! !
  343.  
  344. !Calculator methodsFor: 'private'!
  345.  
  346. addToDisplay: aNumber 
  347.     "Adds a number to the display value. The model has    
  348.      changed."
  349.  
  350.     | temp |
  351.     doneOperation ifTrue: [
  352.         displayValue := 0.
  353.         doneOperation := false].
  354.     displayValue < 0
  355.         ifTrue: [temp := aNumber negated]
  356.         ifFalse: [temp := aNumber].
  357.     pointPlaces ~= 0
  358.         ifTrue: [
  359.             self makeDisplay: displayValue +
  360.                 (temp / (10 raisedToInteger: pointPlaces)).
  361.             pointPlaces := pointPlaces + 1]
  362.         ifFalse: [self makeDisplay: displayValue * 10 + temp]!
  363.  
  364. doOperation
  365.     "Do the current operation (if any).  Update the display
  366.      and accumulator."
  367.  
  368.     currentOperation notNil ifTrue: [
  369.         self performOperation: currentOperation.
  370.         self makeDisplay: accumulatorValue].
  371.     trailingZeros := 0.
  372.     pointPlaces := 0.
  373.     doneOperation := true.
  374.     accumulatorValue := displayValue.!
  375.  
  376. makeDisplay: aNumber 
  377.     "Makes aNumber the display value. The model has changed."
  378.  
  379.     aNumber >= (10 raisedToInteger: noOfDigits)
  380.         ifTrue: [errorFlag := true]
  381.         ifFalse: [displayValue := aNumber].
  382.     self changed!
  383.  
  384. performOperation: aSymbol 
  385.     "Perform the operation given by aSymbol."
  386.  
  387.     aSymbol = #addition ifTrue: [
  388.         accumulatorValue := accumulatorValue + displayValue].
  389.     aSymbol = #subtraction ifTrue: [
  390.         accumulatorValue := accumulatorValue - displayValue].
  391.     aSymbol = #multiplication ifTrue: [
  392.         accumulatorValue := accumulatorValue * displayValue].
  393.     aSymbol = #division ifTrue: [
  394.         displayValue = 0
  395.             ifTrue: [errorFlag := true.]
  396.             ifFalse: [accumulatorValue := accumulatorValue / displayValue]].
  397.     aSymbol = #raiseTo ifTrue: [
  398.         accumulatorValue := accumulatorValue raisedTo: displayValue].
  399.     pointPlaces := 0! !
  400.  
  401. !Calculator methodsFor: 'views'!
  402.  
  403. openCalculatorView
  404.     "Calculator new openCalculatorView"
  405.     | keypadView window digitView comp  extent |
  406.     extent := 230 @ 150.
  407.     keypadView := KeypadView model: self.
  408.     digitView := DigitView model: self.
  409.     comp := CompositePart new.
  410.     comp add: digitView borderedIn:
  411.         (LayoutFrame
  412.             leftFraction: 0 offset: 0 rightFraction: 1 offset: 0
  413.             topFraction: 0 offset: 0 bottomFraction: 0 offset: 40).
  414.     comp add: keypadView borderedIn:
  415.         (LayoutFrame
  416.             leftFraction: 0 offset: 0 rightFraction: 1 offset: 0
  417.             topFraction: 0 offset: 40 bottomFraction: 1 offset: 0).
  418.     window := ScheduledWindow new.
  419.     window label: 'Calculator'.
  420.     window component: comp.
  421.     window minimumSize: extent.
  422.     window open!
  423.  
  424. openDigitView
  425.     "Calculator new openDigitView"
  426.     | digitView window wrapper |
  427.     digitView := DigitView model: self.
  428.     window := ScheduledWindow new.
  429.     window label: 'Digits'.
  430.     wrapper := BorderedWrapper on: digitView.
  431.     wrapper insideColor: ColorValue white.
  432.     window component: wrapper.
  433.     window open!
  434.  
  435. openKeypadView
  436.     "Calculator new openKeypadView"
  437.     | keypadView window |
  438.     keypadView := KeypadView model: self.
  439.     window := ScheduledWindow new.
  440.     window label: 'Keypad'.
  441.     window component: keypadView.
  442.     window open! !
  443. "-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- "!
  444.  
  445. Calculator class
  446.     instanceVariableNames: ''!
  447.  
  448.  
  449. !Calculator class methodsFor: 'instance creation'!
  450.  
  451. new
  452.     "Create a new initialized instance of the receiver."
  453.  
  454.     ^super new initialize!
  455.  
  456. new: aNumber
  457.     "Create a new instance of the receiver, with aNumber digits."
  458.  
  459.     ^self new noOfDigits: aNumber!
  460.  
  461. openView
  462.     "Calculator openView"
  463.     Calculator new openCalculatorView! !
  464.  
  465. !Calculator class methodsFor: 'class initialization'!
  466.  
  467. initialize
  468.     "Initialize default values."
  469.     "Calculator initialize."
  470.  
  471.     DefaultNoOfDigits := 12! !
  472.  
  473.  
  474. !Image class methodsFor: 'instance creation'!
  475.  
  476. extent: extentPoint depth: depth palette: palette words: aWordArray
  477.     "Answer an Image with the specified extent, palette, and bitmap contents.
  478.     The bitmap contents should be specified by a WordArray in the standard
  479.     bitmap format: Z pixel format (contiguous pixel bits), MS byte order and bit
  480.     order, scanline padding to a multiple of 32 bits, and bitsPerPixel either 1,
  481.     2, 4, 8, or 24. The bitsPerPixel should be the lowest supported value >= depth."
  482.     | aByteArray |
  483.     aByteArray := ByteArray new: aWordArray size * 2.
  484.     1 to: aWordArray size do: [:i |
  485.     aByteArray at: i * 2  put: ((aWordArray at: i) bitAnd: 255).
  486.     aByteArray at: i * 2 - 1 put: ((aWordArray at: i) bitShift: -8)].
  487.     ^self extent: extentPoint depth: depth palette: palette bits: aByteArray pad: 16! !
  488.  
  489.  
  490. LabeledBooleanView subclass: #ButtonView
  491.     instanceVariableNames: 'key '
  492.     classVariableNames: ''
  493.     poolDictionaries: ''
  494.     category: 'Calculator'!
  495.  
  496.  
  497. !ButtonView methodsFor: 'accessing'!
  498.  
  499. containsKey: aChar
  500.     "Test to see if aChar is the same as mine own"
  501.     ^key = aChar!
  502.  
  503. key: aChar
  504.     "Set my char"
  505.     key := aChar!
  506.  
  507. sendMessage
  508.     "I've been 'pressed' "
  509.     model value: true! !
  510.  
  511.  
  512. View subclass: #DigitView
  513.     instanceVariableNames: 'sevenSegImages cachedImageSize '
  514.     classVariableNames: ''
  515.     poolDictionaries: ''
  516.     category: 'Calculator'!
  517.  
  518.  
  519. !DigitView methodsFor: 'initialize-release'!
  520.  
  521. initialize
  522.  
  523.     super initialize.
  524.     sevenSegImages := Array new: 12.! !
  525.  
  526. !DigitView methodsFor: 'displaying'!
  527.  
  528. displayErrorOn: aGraphicsContext
  529.     "Display the error indication - a letter E."
  530.  
  531.     ((sevenSegImages at: 12) notNil and: [cachedImageSize = self currentImageSize])
  532.             ifFalse: [self newCachedImages].
  533.     (sevenSegImages at: 12) displayOn: aGraphicsContext at: self digitDisplayOrigin!
  534.  
  535. displayLeftDigits: aCollection on: aGraphicsContext
  536.     "Display, in seven segment form, the digits in aCollection,
  537.      starting from the left."
  538.  
  539.     "The first field in the array is the right (least significant) digit."
  540.  
  541.     | origin width |
  542.     ((sevenSegImages at: 1) isNil or: [cachedImageSize ~= self currentImageSize])
  543.             ifTrue: [self newCachedImages].
  544.     width := self digitBox width.
  545.     origin := self digitDisplayOrigin.
  546.     1 to: aCollection size do: [ :count |
  547.         (sevenSegImages at: (aCollection at: count)  truncated + 1)
  548.             displayOn: aGraphicsContext at: origin + (count * width@0) ]!
  549.  
  550. displayLeftPoint: aPosition on: aGraphicsContext
  551.     "Display a small black image representing the decimal point,
  552.      in a left justified manner."
  553.  
  554.     | origin  width size   |
  555.     width := self digitBox width.
  556.     size := width // 8 max: 2.
  557.     origin := self bounds origin +
  558.         (width * aPosition@0) +
  559.         ((size negated // 2)@(self formBox height + self digitOffset y - size)).
  560.     self point displayOn: aGraphicsContext at: origin!
  561.  
  562. displayMinusOn: aGraphicsContext
  563.     "Display the minus sign."
  564.  
  565.     ((sevenSegImages at: 11) notNil
  566.         and: [cachedImageSize = self currentImageSize])
  567.             ifFalse: [self newCachedImages].
  568.     (sevenSegImages at: 11) displayOn: aGraphicsContext at: self digitDisplayOrigin!
  569.  
  570. displayOn: aGraphicsContext
  571.     "Display the displayValue in seven-segment form."
  572.  
  573.     | digits value remainder count noOfDigits pointPosition trailingZeros |
  574.     model errorFlag ifTrue: [^self displayErrorOn: aGraphicsContext].
  575.     value := model displayValue.
  576.     value < 0 ifTrue: [
  577.         self displayMinusOn: aGraphicsContext.
  578.         value := value negated].
  579.     noOfDigits := model noOfDigits.
  580.     digits := OrderedCollection new: noOfDigits.
  581.     count := 1.
  582.     remainder := value - value truncated.
  583.     [value >= 10] whileTrue: [
  584.         digits addFirst: value \\ 10.
  585.         value := value // 10.
  586.         count := count + 1].
  587.     digits addFirst: value truncated.
  588.     count := count + 1.
  589.     pointPosition := count.
  590.     [(count <= noOfDigits) & (remainder ~= 0)] whileTrue: [
  591.         remainder := remainder * 10.
  592.         value := remainder truncated.
  593.         digits addLast: value.
  594.         remainder := remainder - value.
  595.         count := count + 1].
  596.     trailingZeros := model trailingZeros.
  597.     [(count <= noOfDigits) & (trailingZeros > 0)] whileTrue: [
  598.         digits addLast: 0.
  599.         trailingZeros := trailingZeros - 1.
  600.         count := count + 1].
  601.     model leftJustify
  602.       ifTrue: [
  603.         self displayLeftDigits: digits on: aGraphicsContext.
  604.         self displayLeftPoint: pointPosition on: aGraphicsContext]
  605.       ifFalse: [
  606.         self displayRightDigits: digits on: aGraphicsContext.
  607.         self displayRightPoint: (count -  pointPosition) on: aGraphicsContext].!
  608.  
  609. displayRightDigits: aCollection on: aGraphicsContext
  610.     "Display, in seven segment form, the digits in aCollection,  
  611.      starting from the right hand side."
  612.     "The first field in the array is the right (least significant) digit."
  613.  
  614.     | origin width noOfDigits |
  615.     width := self digitBox width.
  616.     origin := self digitDisplayOrigin.
  617.     ((sevenSegImages at: 1) isNil or: [cachedImageSize ~= self currentImageSize])
  618.             ifTrue: [self newCachedImages].
  619.     noOfDigits := model noOfDigits + 1.
  620.     aCollection size to: 1 by: -1 do: [:count |
  621.         (sevenSegImages at: (aCollection at: aCollection size - count + 1) truncated + 1)
  622.                 displayOn: aGraphicsContext at: origin + (noOfDigits - count * width @ 0)]!
  623.  
  624. displayRightPoint: aPosition on: aGraphicsContext 
  625.     "Display a small black form representing the decimal point, 
  626.     suitable for a right justified display."
  627.  
  628.     | origin width size position |
  629.     position := model noOfDigits + 1 - aPosition.
  630.     width := self digitBox width.
  631.     size := width // 8 max: 2.
  632.     origin := self bounds origin + (width * position @ 0) + (size negated // 2 @ (self formBox height + self digitOffset y - size)).
  633.     self point displayOn: aGraphicsContext at: origin!
  634.  
  635. update: aParameter
  636.  
  637.     self invalidate! !
  638.  
  639. !DigitView methodsFor: 'private'!
  640.  
  641. currentImageSize
  642.     "Answer with the new current image size."
  643.  
  644.     ^self formBox extent!
  645.  
  646. defaultControllerClass
  647.     ^DigitController!
  648.  
  649. digitBox
  650.     "Answers a rectangle which is the box used to divide up
  651.      the calculator digit display area."
  652.  
  653.     | box |
  654.     box := self bounds copy.
  655.     box width: (box width // (model noOfDigits +1)).
  656.     ^box!
  657.  
  658. digitDisplayOrigin
  659.     "Answer the origin for a digit display form for display."
  660.  
  661.     ^(self bounds origin) + (self digitOffset)!
  662.  
  663. digitOffset
  664.     "Offset between digitBox and formBox."
  665.  
  666.     ^(self formBox origin) - (self digitBox origin)!
  667.  
  668. formBox
  669.     "Answers a rectangle which is the box used to display the
  670.      calculator digits."
  671.  
  672.     | box |
  673.     box := self digitBox.
  674.     ^box insetBy: (box width // 8 max: 3)@(box height // 8 max: 3)!
  675.  
  676. newCachedImages
  677.     "Re-calculates all the cached images."
  678.  
  679.     | size halfHeight lineWidth |
  680.     size := self currentImageSize.
  681.     lineWidth := (size x / 8 max: 2)
  682.                 roundTo: 2.
  683.     halfHeight := size y / 2 + (lineWidth / 2).
  684.     0 to: 11 do: 
  685.         [:count | 
  686.         |  mask gc | 
  687.         mask := Mask extent: size.
  688.         gc := mask graphicsContext.
  689.         (#(1 3 4 5 7 9 10 ) includes: count)
  690.             ifFalse: ["Bottom Left Segment."
  691.                 gc displayRectangle: (0 @ (size y - halfHeight + 1) extent: lineWidth @ halfHeight)].
  692.         (#(2 10 11 ) includes: count)
  693.             ifFalse: ["Bottom Right Segment."
  694.                 gc displayRectangle: (size x - lineWidth @ (size y - halfHeight + 1) extent: lineWidth @ halfHeight)].
  695.         (#(1 4 7 10 ) includes: count)
  696.             ifFalse: ["Bottom Segment."
  697.                 gc displayRectangle: (0 @ (size y - lineWidth) extent: size x @ lineWidth)].
  698.         (#(0 1 7 ) includes: count)
  699.             ifFalse: ["Center Segment."
  700.                 gc displayRectangle: (0 @ (size y - halfHeight) extent: size x @ lineWidth)].
  701.         (#(1 2 3 7 10 ) includes: count)
  702.             ifFalse: ["Top Left Segment."
  703.                 gc displayRectangle: (0 @ 0 extent: lineWidth @ halfHeight)].
  704.         (#(5 6 10 11 ) includes: count)
  705.             ifFalse: ["Top Right Segment."
  706.                 gc displayRectangle: (size x - lineWidth @ 0 extent: lineWidth @ halfHeight)].
  707.         (#(1 4 10 ) includes: count)
  708.             ifFalse: ["Top Segment."
  709.                 gc displayRectangle: (0 @ 0 extent: size x @ lineWidth)].
  710.         sevenSegImages at: count + 1 put: mask].
  711.     cachedImageSize := size!
  712.  
  713. point
  714.     | width size |
  715.     width := self digitBox width.
  716.     size := width // 8 max: 2.
  717.     ^Image
  718.         extent: size @ size
  719.         depth: 1
  720.         palette: MappedPalette blackWhite! !
  721.  
  722.  
  723. ControllerWithMenu subclass: #DigitController
  724.     instanceVariableNames: ''
  725.     classVariableNames: ''
  726.     poolDictionaries: ''
  727.     category: 'Calculator'!
  728.  
  729.  
  730. !DigitController methodsFor: 'menu messages'!
  731.  
  732. changeDigitLength
  733.     "Change the number of digits displayed."
  734.  
  735.     | answerString |
  736.     answerString := DialogView
  737.                 request: 'Number of digits to be displayed?'
  738.                 initialAnswer: model noOfDigits printString.
  739.     answerString isEmpty ifFalse: [
  740.         model noOfDigits: (Number readFrom: (ReadStream on: answerString))]!
  741.  
  742. setLeftJustify
  743.     "Display as left-justified."
  744.  
  745.     model leftJustify ifFalse: [model leftJustify: true]!
  746.  
  747. setRightJustify
  748.     "Display as right-justified."
  749.  
  750.     model leftJustify ifTrue: [model leftJustify: false]! !
  751.  
  752. !DigitController methodsFor: 'private'!
  753.  
  754. menu
  755.     ^PopUpMenu
  756.             labels: ' Digit Length \ Left Justify \ Right Justify ' withCRs
  757.             lines: #(1)
  758.             values:  #(changeDigitLength setLeftJustify setRightJustify).! !
  759.  
  760.  
  761. Controller subclass: #KeypadViewController
  762.     instanceVariableNames: ''
  763.     classVariableNames: ''
  764.     poolDictionaries: ''
  765.     category: 'Calculator'!
  766.  
  767.  
  768. !KeypadViewController methodsFor: 'control'!
  769.  
  770. viewWantingControl
  771.     "If the keyboard has been pressed, then deal with it. Otherwise return the object under the cursor"
  772.  
  773.     (self isControlWanted and: [self sensor keyboardPressed])
  774.         ifTrue:
  775.             [self processKeyboard].
  776.     ^view componentWantingControl! !
  777.  
  778. !KeypadViewController methodsFor: 'private'!
  779.  
  780. processKeyboard
  781.     "The user typed a key on the keyboard.  Tell the appropriate button  that it
  782.     is selected by this key."
  783.  
  784.     |button |
  785.     button := view subViewContainingCharacter: sensor keyboard.
  786.     button notNil
  787.         ifTrue: [button  sendMessage]
  788.         ifFalse: [Screen default ringBell]! !
  789.  
  790. Calculator initialize!
  791.  
  792. KeypadView initialize!
  793.  
  794.